package com.pzl.projection.push;

import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.projection.MediaProjection;
import android.util.Log;
import android.view.Surface;

import java.nio.ByteBuffer;

import static android.media.MediaFormat.KEY_BIT_RATE;
import static android.media.MediaFormat.KEY_FRAME_RATE;
import static android.media.MediaFormat.KEY_I_FRAME_INTERVAL;

/**
 * 屏幕数据编码类
 */
public class ScreenDisplayH265Codec extends Thread {

    final String TAG = "jinx_projection";

    private static ScreenDisplayH265Codec instance;

    //系统屏幕数据操作接口
    MediaProjection mediaProjection;

    //DSP硬编解码器
    private MediaCodec mediaCodec;
    private Surface surface;
    private VirtualDisplay virtualDisplay;


    private ScreenDisplayH265Codec() {
    }

    public static ScreenDisplayH265Codec getInstance() {
        if (instance == null) instance = new ScreenDisplayH265Codec();
        return instance;
    }


    public void init(MediaProjection mediaProjection, int width, int height) {
        this.mediaProjection = mediaProjection;

        try {
            //h265
            MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_HEVC, width, height);
            format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
            //比特率
            format.setInteger(KEY_BIT_RATE, width * height);
            //帧率
            format.setInteger(KEY_FRAME_RATE, 20);
            format.setInteger(KEY_I_FRAME_INTERVAL, 1);
            mediaCodec = MediaCodec.createEncoderByType("video/hevc");
            mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            //使用surface数据encode代替input buffers
            surface = mediaCodec.createInputSurface();
        } catch (Exception e) {
            Log.i(TAG, e.getMessage() + "   也许你的设备不支持H265 DSP");
        }
        //创建场地
        virtualDisplay = mediaProjection.createVirtualDisplay(
                "-display",
                width, height, 1,
                DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, surface, null, null);

    }

    @Override
    public void run() {
        mediaCodec.start();
        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        while (true) {
            try {
                int outputBufferId = mediaCodec.dequeueOutputBuffer(bufferInfo, 10000);
                if (outputBufferId >= 0) {
                    ByteBuffer byteBuffer = mediaCodec.getOutputBuffer(outputBufferId);
                    handleFrameData(byteBuffer, bufferInfo);
                    mediaCodec.releaseOutputBuffer(outputBufferId, false);
                }
            } catch (Exception e) {
                e.printStackTrace();
                break;
            }
        }
    }


    public static final int FRAME_TYPE_NAL_I = 19;//I帧
    public static final int FRAME_TYPE_NAL_VPS_SPS_PPS = 32;//h265 -> VPS+SPS+PPS (vps：面向未来 3d vr视频相关信息等)
    private byte[] vps_sps_pps_buf;//缓存视频配置信息

    //处理一帧数据
    private void handleFrameData(ByteBuffer frameData, MediaCodec.BufferInfo bufferInfo) {
        int offset = 4;//帧数据分隔符 类型1：0x00,0x00,0x00,0x01
        if (frameData.get(2) == 0x01) {
            offset = 3;//帧数据分隔符 类型2：0x00,0x00,0x01
        }
        //帧分隔符后面为帧类型数据
        //计算取出帧类型
        int frameType = (frameData.get(offset) & 0x7E) >> 1;
        if (frameType == FRAME_TYPE_NAL_VPS_SPS_PPS) {
            vps_sps_pps_buf = new byte[bufferInfo.size];
            frameData.get(vps_sps_pps_buf);
        } else if (frameType == FRAME_TYPE_NAL_I) {//视频文件只在第一个I帧具有vps、sps、pps等相关配置信息，为视频流传输过程能顺利在播放端解析，每个I帧手动添加配置信息

            final byte[] bytes = new byte[bufferInfo.size];
            frameData.get(bytes);

            byte[] newBuf = new byte[vps_sps_pps_buf.length + bytes.length];
            System.arraycopy(vps_sps_pps_buf, 0, newBuf, 0, vps_sps_pps_buf.length);
            System.arraycopy(bytes, 0, newBuf, vps_sps_pps_buf.length, bytes.length);

            screenDisplayDispatcher.onReceiveFrameData(newBuf);
        } else {//普通帧数据 P帧，B帧
            final byte[] bytes = new byte[bufferInfo.size];
            frameData.get(bytes);

            screenDisplayDispatcher.onReceiveFrameData(bytes);
        }
    }


    private IScreenDisplay screenDisplayDispatcher;

    public void registerDataReceiver(IScreenDisplay screenDisplay) {
        this.screenDisplayDispatcher = screenDisplay;
    }

    public static interface IScreenDisplay {
        /**
         * 获取一帧数据
         *
         * @param frameData
         */
        void onReceiveFrameData(byte[] frameData);
    }


}
